package com.zhenai.jmeter;


import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ReferenceConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.rpc.service.GenericService;
import com.alibaba.fastjson.JSON;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.log.Logger;

import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;


public class GenericInvokeService extends AbstractJavaSamplerClient {
    private Logger log=getLogger();
    private GenericService genericService = null;
    private ServiceInvokeParam invokeParam = null;

    @Override
    public Arguments getDefaultParameters() {
        Arguments params = new Arguments();
        params.addArgument("env","daily");
        params.addArgument("service", "com.zhenai.work.datafactory.api.DataStoreReadService");
        params.addArgument("version", "1.0.0");
        params.addArgument("targetIp", "127.0.0.1");
        params.addArgument("targetPort", "21880");
        params.addArgument("invokeParam", "{\"methodSignature\":\"findById~java.lang.Long\",\"targetIp\":null,\"customTimeout\":0,\"targetProject\":null,\"useShadowDB\":false,\"convertInternalClass\":true,\"invokeArgs\":[3],\"attachments\":null}");
        return params;
    }

    @Override
    public SampleResult runTest(JavaSamplerContext context) {
        log.info("---begin run----");
        SampleResult sr = new SampleResult();
        try {
            sr.sampleStart();
            final String methodSignature = invokeParam.getMethodSignature();
            final Object[] invokeArgs = invokeParam.getInvokeArgs();
            final boolean convertInternalClass = invokeParam.isConvertInternalClass();
            Object invokeResult = genericService.$invoke(MethodSignatureUtils.findMethodName(methodSignature),
                    convertParameterTypes(methodSignature, convertInternalClass),
                    convertParameterValues(invokeArgs, convertInternalClass));
            log.debug("from provider:" + invokeResult.toString());
            sr.setResponseData(invokeResult.toString(), null);
            sr.setDataType(SampleResult.TEXT);
            sr.setSuccessful(true);
            sr.sampleEnd();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("invoke failed:" + e.getMessage());
        }
        log.debug("---end run----");
        return sr;
    }

    private String[] convertParameterTypes(String methodSignature, boolean convertInternalClass) {
        String[] types = MethodSignatureUtils.findParameterTypes(methodSignature);
        if (types != null) {
            for (int i = 0; i < types.length; i++) {
                types[i] = MethodSignatureUtils.eraseGenericType(types[i]);
                if (convertInternalClass) {
                    types[i] = MethodSignatureUtils.convertInternalClassStr(types[i]);
                }
            }
        }
        return types;
    }

    private Object[] convertParameterValues(Object[] parameters, boolean convertInternalClass) {
        return convertInternalClass ? GenericInvokeUtils.convertInternalClass(parameters) : parameters;
    }

    @Override
    public void setupTest(JavaSamplerContext context) {
        try {
            log.debug("---begin setup----");
            String parameter = context.getParameter("invokeParam");
           // String parameter ="{\"methodSignature\":\"findById~java.lang.Long\",\"targetIp\":null,\"customTimeout\":0,\"targetProject\":null,\"useShadowDB\":false,\"convertInternalClass\":true,\"invokeArgs\":[3],\"attachments\":null}";
            invokeParam = JSON.parseObject(parameter, ServiceInvokeParam.class);
            invokeParam.setDataId(context.getParameter("service"));
            invokeParam.setTargetIp(context.getParameter("targetIp"));

            ApplicationConfig applicationConfig = new ApplicationConfig();
            applicationConfig.setName("zhenai-jmeter-dubbo");
            //这里配置了dubbo的application信息*(demo只配置了name)*，因此demo没有额外的dubbo.xml配置文件
            RegistryConfig registryConfig = new RegistryConfig();
            registryConfig.setAddress("zookeeper://127.0.0.1:2181");
            //这里配置dubbo的注册中心信息，因此demo没有额外的dubbo.xml配置文件
            String interfaceClass = invokeParam.getDataId();
            ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
            reference.setApplication(applicationConfig);
            reference.setRegistry(registryConfig);
            reference.setInterface(interfaceClass); // 接口名
            reference.setGeneric(true); // 声明为泛化接口

            String version = context.getParameter("version");
            reference.setVersion(version);
            reference.setTimeout(30000);

      /*      ReferenceConfig实例很重，封装了与注册中心的连接以及与提供者的连接，
            需要缓存，否则重复生成ReferenceConfig可能造成性能问题并且会有内存和连接泄漏。
            API方式编程时，容易忽略此问题。
            这里使用dubbo内置的简单缓存工具类进行缓存*/
//            ReferenceConfigCache cache = ReferenceConfigCache.getCache();
//            GenericService genericService = cache.get(reference);
//            if (genericService==null) {
//                genericService = reference.get();
//            }

            String directUrl = "dubbo://"+invokeParam.getTargetIp() + ":" + context.getParameter("targetPort");
            reference.setUrl(directUrl);
            this.genericService  = reference.get();
            log.debug("---end setup----");
        } catch (Exception e) {
            e.printStackTrace();
            log.error("invoke failed:" + e.getMessage());
        }
    }


    public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        GenericInvokeService service = new GenericInvokeService();

        Arguments params = new Arguments();
        params.addArgument("testString", "hehe");
        JavaSamplerContext context = new JavaSamplerContext(params);

        service.setupTest(context);
        SampleResult res = service.runTest(context);
        System.out.println("test is success:" + res.isSuccessful() + " used:" + (res.getEndTime() - res.getStartTime())
                + "ms " + " result:" + res.getSampleLabel() + ":" + res.getSamplerData());
        service.teardownTest(context);

        System.out.println("res.getEndTime() = " + res.getEndTime());
        System.out.println("res.getStartTime() = " + res.getStartTime());
        System.exit(0);
    }

}
